package com.bluedot.www.core.service;


import com.bluedot.www.core.common.HandwritingRuntimeException;
import com.bluedot.www.core.common.OSSConfigure;
import com.bluedot.www.core.common.ResultCodeEnum;
import com.bluedot.www.core.dao.Execute;
import com.bluedot.www.core.pojo.BO.HandwritingFeatureBo;
import com.bluedot.www.core.pojo.DO.*;
import com.bluedot.www.core.pojo.DTO.AnalyzeBatchResultDTO;
import com.bluedot.www.core.pojo.DTO.CharacterReportDTO;
import com.bluedot.www.core.pojo.DTO.ImageListDTO;
import com.bluedot.www.core.pojo.DTO.ReportHistoryDTO;
import com.bluedot.www.core.utils.AlgorithmCutoverUtils;
import com.bluedot.www.core.utils.OSSManageUtil;
import com.bluedot.www.core.utils.WordUtil;
import com.bluedot.www.core.utils.ZipUtil;
import com.bluedot.www.framework.mvc.servlet.http.MultipartFile;
import com.bluedot.www.framework.mvc.servlet.http.ResponseEntity;
import com.spire.doc.Document;
import com.spire.doc.FileFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 性格报告表 服务类
 * </p>
 *
 * @author zhouxuan
 * @since 2021-08-07
 */
public class CharacterReportService {

    /**性格报告Mapper*/
    private static final String CHARACTER_REPORT_MAPPER_ADDRESS = "com.bluedot.www.core.mapper.xml.CharacterReportMapper.";
    Logger logger = LoggerFactory.getLogger(CharacterReportService.class);
    /**word 模板位置-相对位置*/
    private static final String WORD_TEMPLATE_LOCATION = "static/reportTemplate.docx";
    /**Word 文档中笔迹图片的占位符*/
    private static final String HANDWRITING_IMAGES_MARK = "${handwritingImages}";
    /**单次生成性格的最大数量*/
    private static final int MAX_ANALYZE_NUM = 30;
    /**阿里云 URL 头*/
    private static final String OSS_ACCESS_URL = OSSConfigure.getInstance().getAccessUrl();

    public static Object execute(String methodName, Object[] param) {
        CharacterReportService characterReportService = new CharacterReportService();
        Object result;
        switch (methodName) {
            case "analyze":
                result = characterReportService.analyze((List<MultipartFile>) param[0], (Long) param[1], (Long) param[2]);
                break;
            case "queryReportByUserId":
                result = characterReportService.queryReportByUserId((Long) param[0]);
                break;
            case "queryReportDetails":
                result = characterReportService.queryReportDetails((Long) param[0]);
                break;
            case "deleteReportById":
                result = characterReportService.deleteReportById((List<Long>) param[0]);
                break;
            case "findDeleteIdByReportId":
                result = characterReportService.findDeleteIdByReportId((Long) param[0]);
                break;
            case "getCharacterReportBatch":
                result = characterReportService.getCharacterReportBatch((List<Long>) param[0]);
                break;
            case "analyzeBatch":
                result = characterReportService.analyzeBatch((List<Long>) param[0], (Long) param[1]);
                break;
            case "insertReportInUser":
                result = characterReportService.insertReportInUser((CharacterReport) param[0]);
                break;
            case "insertReportInCustomer":
                result = characterReportService.insertReportInCustomer((CharacterReport) param[0]);
                break;
            case "deleteReportByIdLogic":
                result = characterReportService.deleteReportByIdLogic((List<Long>) param[0]);
                break;
            case "queryReportHistoryCondition":
                result = characterReportService.queryReportHistoryCondition((Long) param[0], (Integer) param[1],
                        (Integer) param[2]);
                break;
            case "analyzeBatchNoAnalysis":
                result = characterReportService.analyzeBatchNoAnalysis((List<Long>) param[0], (Long) param[1]);
                break;
            default:
                throw new HandwritingRuntimeException(ResultCodeEnum.METHOD_NOT_FOUND.getCode(),
                        "Method NOT FOUND, method name: " + methodName);

        }
        return result;
    }

    /**
     * 普通用户和心理咨询师插入性格报告
     * @param characterReport: 性格报告初始值（不包括：reportLink, score, summary）
     * @return boolean
     * @author He Peng
     * @date 2021/10/26 8:43
     */
    private boolean insertReportInUser(CharacterReport characterReport) {
        if (characterReport.getUserId() != null && characterReport.getCustomerId() == null) {
            Integer res = (Integer) Execute.execute(Execute.UPDATE_TYPE,
                    CHARACTER_REPORT_MAPPER_ADDRESS + "insertReportInUser", new Object[]{characterReport});
            return res > 0;
        }
        return false;
    }

    /**
     * 客户插入性格报告
     * @param characterReport: 性格报告
     * @return boolean
     * @author He Peng
     * @date 2021/10/26 8:47
     */
    private boolean insertReportInCustomer(CharacterReport characterReport) {
        if (characterReport.getCustomerId() != null && characterReport.getUserId() == null) {
            Integer res = (Integer) Execute.execute(Execute.UPDATE_TYPE,
                    CHARACTER_REPORT_MAPPER_ADDRESS + "insertReportInCustomer", new Object[]{characterReport});
            return res > 0;
        }
        return false;
    }

    /**
     * 预处理完成后生成一个性格报告编号，根据这个编号获取笔迹图片，然后生成性格报告
     *
     * @param reportId 性格报告编号
     * @return CharacterReportDTO 性格报告信息摘要
     * @author GUOZHIPENG & He Peng
     * @date 2021/9/1 16:50
     */
    private CharacterReportDTO analyze(List<MultipartFile> pretreatedImgList, Long reportId, Long anaId) {
        CharacterReport report = queryReportDetails(reportId);
        CharacterReportDTO reportDTO = new CharacterReportDTO();
        reportDTO.setTime(LocalDate.now().toString());
        reportDTO.setId(String.valueOf(report.getId()));
        // 0. 获取用户信息 用户名
        Object info;
        if (report.getCustomerId() == null) {
            info = UserService.execute("findUserByUserId", new Object[]{report.getUserId()});
        } else {
            info = CustomerService.execute("findCustomerById", new Object[]{report.getCustomerId()});
        }
        // 1. 根据指定的算法编号，获取相应算法，然后调用生成笔迹特征值，并保存至数据库
        List<InputStream> imageInputStreamList = new ArrayList<>(6);
        pretreatedImgList.forEach(multipartFile -> {
            try {
                imageInputStreamList.add(multipartFile.getInputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        return getCharacterReportDTO(reportId, anaId, info, imageInputStreamList);
    }

    /**
     * 根据指定的算法编号，获取相应算法，然后调用生成笔迹特征值，并保存至数据库
     * @param reportId: 性格报告编号
     * @param anaId: 分析算法编号
     * @param info: 用户信息
     * @param imageInputStreamList: 图片输入流集合
     * @return com.bluedot.www.core.pojo.DTO.CharacterReportDTO
     * @author He Peng
     * @date 2021/10/28 8:34
     */
    private CharacterReportDTO getCharacterReportDTO(Long reportId, Long anaId, Object info, List<InputStream> imageInputStreamList) {
        // 0. 获取信息
        String name = null;
        if (info instanceof User) {
            User user = (User) info;
            name = user.getName();
        } else if (info instanceof Customer) {
            Customer customer = (Customer) info;
            name = customer.getName();
            // 更新性格报告状态(维护一个字段太麻烦了，如果此状态会因为另外一个表的改变而改变，那就不应该使用字段进行记录)
            List<Long> tmp = new ArrayList<>(1);
            tmp.add(customer.getId());
            CustomerService.execute("updateCustomerStatusList", new Object[]{tmp});
        } else {
            logger.info("无法处理的对象类型：" + info);
        }

        // 1. 更新分析算法编号，分析得到笔迹特征并存储至数据库中
        updateReportAnaId(reportId, anaId);
        analyzeImage(imageInputStreamList, reportId, anaId);
        // 2. 查询出所有笔迹图片的笔迹特征的平均值
        HandwritingFeatureBo feature = (HandwritingFeatureBo) HandwritingFeatureService.execute(
                "getAverageFeature", new Object[]{reportId});
        // 3. 根据笔迹特征值生成性格报告
        ByteArrayOutputStream os = generateCharacterReport(feature, info, imageInputStreamList);
        // 4. 上传性格报告至阿里云OSS，并将链接保存至数据库
        String filename = name + "-性格报告-"+ LocalDate.now().toString() + ".docx";
        String reportLink = OSSManageUtil.upload(new ByteArrayInputStream(os.toByteArray()),
                filename, "bluedot/report/", false);
        boolean res = updateReportLink(reportId, reportLink);
        if (!res) {
            logger.error("错误，dao 层语句执行失败， 方法名：updateReportLink");
        }
        return setReportContent(String.valueOf(reportId), info, OSS_ACCESS_URL + reportLink, feature);
    }

    /**
     * 设置报告内容
     * @return com.bluedot.www.core.pojo.DTO.CharacterReportDTO
     * @author He Peng
     * @date 2021/10/29 8:40
     */
    private CharacterReportDTO setReportContent(String reportId, Object info,String reportLink, HandwritingFeatureBo feature) {
        CharacterReportDTO reportDTO = new CharacterReportDTO();
        String name = null;
        String age = null;
        String gender = null;
        if (info instanceof User) {
            User user = (User) info;
            name = user.getName();
            LocalDate now = LocalDate.now();
            LocalDate birthday = LocalDate.parse(user.getBirthday().toString());
            Period period = Period.between(birthday, now);
            age = String.valueOf(period.getYears());
            gender = user.getGender() == 0 ? "男" : "女";
        } else if (info instanceof Customer) {
            Customer customer = (Customer) info;
            name = customer.getName();
            LocalDate now = LocalDate.now();
            LocalDate birthday = LocalDate.parse(customer.getBirthday().toString());
            Period period = Period.between(birthday, now);
            age = String.valueOf(period.getYears());
            gender = customer.getGender() == 0 ? "男" : "女";
        } else {
            logger.info("无法处理的对象类型：" + info);
        }
        reportDTO.setId(reportId);
        reportDTO.setTime(LocalDate.now().toString());
        reportDTO.setAge(age);
        reportDTO.setName(name);
        reportDTO.setSex(gender);
        reportDTO.setCareerAdvice("取已之长，补已之短”学会了这一点将会影响到你的成败及你对工作的喜好。");
        reportDTO.setCardIdentification("聪慧性、乐群性、有恒性、敏感性、忧虑性");
        reportDTO.setCharacterAnalysis("你性格奔放，个性鲜明。浪漫型占主导。有时会脱离实际。有时情感的变化波动大。具有一定个性和脾气；精神饱满，柔中带刚；偏向外界评价，易受周围情绪感染。");
        reportDTO.setNoteCharacteristics("精神状态方面，进取精神较强，力求向上。对人方面，比较热心。处事方面，不拘小节。自尊心和主观性方面，比较谦虚，尊重他人的意见。自信心和果断性方面，个人意志不强。开放创新方面，比较注意条条框框的限制，对新生事物兴趣不高。");
        reportDTO.setPreface("本报告的目的：帮助你开始了解和分析最真实的自己，协助你迈出职业定位和人生规划的第一步，从人格类型的角度描述了个体的适合岗位特质和你的发展建议。");
        reportDTO.setReportLink(reportLink);
        return reportDTO;
    }


    /**
     * 读取图片输入流集合，分析得到笔迹特征，保存至数据库中
     * 注意：输入流已经重置，所以可以继续使用
     * @param imageInputStreamList: 笔迹图片输入流集合
     * @param reportId: 性格报告编号
     * @param anaId: 分析算法编号
     * @author He Peng
     * @date 2021/10/27 22:51
     */
    private void analyzeImage(List<InputStream> imageInputStreamList, Long reportId, Long anaId) {
        List<BufferedImage> imageList = new ArrayList<>();
        // 转化为BufferedImage
        for (InputStream in : imageInputStreamList) {
            try {
                imageList.add(ImageIO.read(in));
                // 重置流,以便可以重复读取
                if (in.markSupported()) {
                    in.reset();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 2. 根据分析算法编号找到指定的分析算法
        Algorithm handle = (Algorithm) AlgorithmService.execute("findAlgorithmById", new Object[]{anaId});
        // 3. 保存笔迹特质值到数据
        int n = 0;
        for (BufferedImage buff : imageList) {
            // 计算得到笔迹特征
            HandwritingFeature result = AlgorithmCutoverUtils.handlerAlgorithm(buff, handle.getLocation());
            List<HandwritingImg> images = getImageByReportId(reportId);
            boolean res = (boolean) HandwritingFeatureService.execute("insertHandwritingFeature",
                    new Object[]{result, images.get(n).getId()});
            if (!res) {
                throw new HandwritingRuntimeException(20001, "保存笔迹特征值失败");
            }
            n++;
        }
    }


    /**
     * 根据性格报告编号 批量分析 生成性格报告
     * @param reportIdList: 性格报告编号集合
     * @return com.bluedot.www.framework.mvc.servlet.http.ResponseEntity
     * @author He Peng
     * @date 2021/10/26 10:41
     */
    private List<AnalyzeBatchResultDTO> analyzeBatch(List<Long> reportIdList, Long anaId) {
        if (reportIdList.size() > MAX_ANALYZE_NUM) {
            throw new HandwritingRuntimeException(ResultCodeEnum.PARAM_ERROR.getCode(),
                    "参数错误，批量处理每次最多分析生成"+ MAX_ANALYZE_NUM +"份性格报告");
        }
        List<AnalyzeBatchResultDTO> analyzeBatchResultDTOList = new ArrayList<>();
        for (Long id : reportIdList) {
            AnalyzeBatchResultDTO resultDTO = analyzeBatchHelper(id);
            analyzeBatchResultDTOList.add(resultDTO);
        }
//        Map<String, InputStream> fileMap = new HashMap<>(16);
//        for (ResponseEntity responseEntity : responseEntityList) {
//            fileMap.put(responseEntity.getFileName(), new ByteArrayInputStream(responseEntity.getFileBytes()));
//        }
//        return reportList;
        return analyzeBatchResultDTOList;
    }

    /**
     * 根据性格报告获取图片相对地址，然后从远程获取图片进行分析，生成性格报告
     * 设置 resultDTO 的
     * @param reportId: 性格报告编号
     * @return CharacterReportDTO
     */
    private AnalyzeBatchResultDTO analyzeBatchHelper(Long reportId) {
        CharacterReport report = queryReportDetails(reportId);
        // 0. 获取用户信息 用户名
        Object info;
        String name;
        if (report.getCustomerId() == null) {
            info = UserService.execute("findUserByUserId", new Object[]{report.getUserId()});
            name = ((User) info).getName();
        } else {
            info = CustomerService.execute("findCustomerById", new Object[]{report.getCustomerId()});
            name = ((Customer) info).getName();
            // 更新性格报告状态(维护一个字段太麻烦了，如果此状态会因为另外一个表的改变而改变，那就不应该使用字段进行记录)
            List<Long> tmp = new ArrayList<>(1);
            tmp.add(((Customer) info).getId());
            CustomerService.execute("updateCustomerStatusList", new Object[]{tmp});
        }
        List<HandwritingImg> handwritingImgList = getImageByReportId(reportId);

        // 1. 根据指定的算法编号，获取相应算法，然后调用生成笔迹特征值，并保存至数据库
        List<InputStream> imageInputStreamList = getImgFeature(handwritingImgList, report.getAnaAlgorithmId());
        // 2. 查询出所有笔迹图片的笔迹特征的平均值
        HandwritingFeatureBo feature = (HandwritingFeatureBo) HandwritingFeatureService.execute(
                "getAverageFeature", new Object[]{reportId});
        // 3. 根据笔迹特征值生成性格报告
        ByteArrayOutputStream os = generateCharacterReport(feature, info, imageInputStreamList);
        // 4. 上传性格报告至阿里云OSS，并将链接保存至数据库
        String filename = name + "-性格报告-"+ LocalDate.now().toString() + ".docx";
        String reportLink = OSSManageUtil.upload(new ByteArrayInputStream(os.toByteArray()),
                filename, "bluedot/report/", false);
        boolean res = updateReportLink(reportId, reportLink);
        if (!res) {
            logger.error("错误，dao 层语句执行失败， 方法名：updateReportLink");
        }
        // 设置 resultDTO的值
        String summary = "这是一段性格报告概括";
        CharacterReportDTO reportDTO = setReportContent(String.valueOf(reportId), info,OSS_ACCESS_URL + reportLink, feature);
        return setAnalyzeBatchResultDTO(reportId, name, summary, handwritingImgList, reportDTO);
    }



    /**
     * 设置 ResultDTO的值 包括 id,name,time,summary, originalUrlList,pretreatmentUrlList, reportDTO
     * @return com.bluedot.www.core.pojo.DTO.AnalyzeBatchResultDTO
     * @author He Peng
     * @date 2021/10/29 15:16
     */
    private AnalyzeBatchResultDTO setAnalyzeBatchResultDTO(Long reportId, String name, String summary,
                                                           List<HandwritingImg> handwritingImgList,
                                                           CharacterReportDTO reportDTO) {
        AnalyzeBatchResultDTO resultDTO = new AnalyzeBatchResultDTO();
        resultDTO.setReportDTO(reportDTO);
        resultDTO.setId(reportId);
        resultDTO.setName(name);
        resultDTO.setSummary(summary);
        if (handwritingImgList.size() > 0) {
            resultDTO.setTime(handwritingImgList.get(0).getWritingTime());
        }
        List<ImageListDTO> originalUrlList = new ArrayList<>();
        List<ImageListDTO> pretreatmentUrlList = new ArrayList<>();
        for (HandwritingImg handwritingImg : handwritingImgList) {
            ImageListDTO original = new ImageListDTO();
            ImageListDTO pretreatment = new ImageListDTO();
            original.setId(handwritingImg.getId());
            original.setUrl(OSS_ACCESS_URL + handwritingImg.getHandwritingImg());
            originalUrlList.add(original);
            pretreatment.setId(handwritingImg.getId());
            pretreatment.setUrl(OSS_ACCESS_URL + handwritingImg.getPretreatmentImg());
            pretreatmentUrlList.add(pretreatment);
        }
        resultDTO.setOriginalUrlList(originalUrlList);
        resultDTO.setPretreatmentUrlList(pretreatmentUrlList);
        return resultDTO;
    }


    /**
     * 批量分析未分析性格报告
     * @param customerIdList
     */
    private List<AnalyzeBatchResultDTO> analyzeBatchNoAnalysis(List<Long> customerIdList, Long anaId) {
        List<Long> reportIdList = new ArrayList<>(30);
        for (Long customerId : customerIdList) {
            reportIdList.addAll(queryCustomerNoAnalysisReport(customerId));
        }
        List<AnalyzeBatchResultDTO> analyzeBatchResultDTOS = analyzeBatch(reportIdList, anaId);
        // 将客户状态更新为没有未分析图片
        for (Long id : customerIdList) {
            CustomerService.execute("updateCustomerStatus", new Object[]{id, 0});
        }
        return analyzeBatchResultDTOS;
    }

    /**
     * 根据性格报告编号查询未分析性格报告编号
     * @param customerId: 客户编号
     * @return java.util.List<java.lang.Long>
     * @author He Peng
     * @date 2021/10/31 19:49
     */
    private List<Long> queryCustomerNoAnalysisReport(Long customerId) {
        return (List<Long>) Execute.execute(Execute.QUERY_TYPE,
                CHARACTER_REPORT_MAPPER_ADDRESS + "queryCustomerNoAnalysisReport", new Object[]{customerId});
    }

    /**
     * 更新分析算法编号
     * @param reportId 性格报告编号
     * @param anaId 分析算法编号
     * @return 操作是否成功
     */
    private boolean updateReportAnaId(Long reportId, Long anaId) {
        Map<String, Object> reportIdMap = new HashMap<>(1);
        Map<String, Object> anaIdMap = new HashMap<>(1);
        reportIdMap.put("reportId", reportId);
        anaIdMap.put("anaId", anaId);
        int res = (int) Execute.execute(Execute.UPDATE_TYPE, CHARACTER_REPORT_MAPPER_ADDRESS + "updateReportAnaId",
                new Object[]{reportIdMap, anaIdMap});
        return res > 0;
    }


    /**
     * 使用指定算法计算笔迹特征值，保存至数据库中
     * @param handleId: 算法编号
     * @author He Peng
     * @date 2021/10/20 20:20
     */
    private List<InputStream> getImgFeature(List<HandwritingImg> images, Long handleId) {
        List<BufferedImage> imageList = new ArrayList<>();
        // 1. 获取笔迹图片(已预处理)
        List<String> pathList = new ArrayList<>();
        assert images != null;
        for (HandwritingImg image : images) {
            pathList.add(image.getHandwritingImg());
        }
        List<InputStream> imageInputStreamList = OSSManageUtil.getFileFromUrlBatch(pathList);
        // 转化为BufferedImage
        for (InputStream in : imageInputStreamList) {
            try {
                imageList.add(ImageIO.read(in));
                // 重置流,以便可以重复读取
                if (in.markSupported()) {
                    in.reset();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (imageList.size() != images.size()) {
            logger.info("存在图片无法根据链接获取图片 \nimageList: " + imageList + "\nimage: " + images);
        }
        // 2. 使用指定的分析算法，计算得到笔迹特征(如果未获取到则使用默认算法)
        Algorithm handle = (Algorithm) AlgorithmService.execute("findAlgorithmById", new Object[]{handleId});
        String location = null;
        if (handle != null) {
            location = handle.getLocation();
        }
        // 3. 保存笔迹特质值到数据
        int n = 0;
        for (BufferedImage buff : imageList) {
            HandwritingFeature result = AlgorithmCutoverUtils.handlerAlgorithm(buff, location);
            boolean res = (boolean) HandwritingFeatureService.execute("insertHandwritingFeature",
                    new Object[]{result, images.get(n).getId()});
            if (!res) {
                throw new HandwritingRuntimeException(20001, "保存笔迹特征值失败");
            }
            n++;
        }
        return imageInputStreamList;
    }


    /**
     * 根据用户id查找报告
     *
     * @param userId 用户id
     * @return com.bluedot.www.core.pojo.DO.CharacterReport
     * @author jiangnan
     * @date 2021/8/8 14:42
     */
    private List<CharacterReport> queryReportByUserId(Long userId) {
        return (List<CharacterReport>) Execute.execute(Execute.QUERY_TYPE, CHARACTER_REPORT_MAPPER_ADDRESS + "queryReportByUserId", new Object[]{userId});
    }


    /**
     * 根据性格报告编号批量获取性格报告（必须已经生成），然后打包为 zip 压缩包
     * @param reportIdList: 性格报告编号集合
     * @return com.bluedot.www.framework.mvc.servlet.http.ResponseEntity
     * @author He Peng
     * @date 2021/10/24 19:41
     */
    private ResponseEntity getCharacterReportBatch(List<Long> reportIdList) {
        List<String> reportLinkList = getReportLinkById(reportIdList);

        Map<String, InputStream> fileMap = OSSManageUtil.getFileInfoFromUrlBatch(reportLinkList);
        String filename = "性格报告-" + LocalDate.now() + ".zip";
        return new ResponseEntity(filename, ZipUtil.zipFiles(fileMap));
    }


    /**
     * 根据性格报告编号获取性格报告的相对地址
     * @param reportIdList: 性格报告编号集合
     * @return java.util.List<java.lang.String> 性格报告相对地址
     * @author He Peng
     * @date 2021/10/25 22:19
     */
    private List<String> getReportLinkById(List<Long> reportIdList) {
        List<String> reportLinkList = new ArrayList<>();
        for (Long reportId : reportIdList) {
            List<CharacterReport> list = (List<CharacterReport>) Execute.execute(Execute.QUERY_TYPE,
                    CHARACTER_REPORT_MAPPER_ADDRESS + "getReportLink", new Object[]{reportId});
            if (list.size() > 0 && list.get(0).getReportLink() != null) {
                reportLinkList.add(list.get(0).getReportLink());
            }
        }
        return reportLinkList;
    }



    /**
     * 查看性格报告详情
     *
     * @param reportId 性格报告id
     * @return void
     * @author jiangnan
     * @date 2021/8/8
     */
    private CharacterReport queryReportDetails(Long reportId) {
        List<CharacterReport> characterReport = (List<CharacterReport>) Execute.execute(Execute.QUERY_TYPE,
                CHARACTER_REPORT_MAPPER_ADDRESS + "queryReportDetails", new Object[]{reportId});
        if (characterReport.size() > 0) {
            return characterReport.get(0);
        } else {
            return null;
        }
    }

    /**
     * 批量删除性格报告
     * 删除性格报告的同时要把预处理和分析图片 以及  笔记特征删除
     *
     * @param reportId 性格报告id
     * @return void
     * @author jiangnan
     * @date 2021/8/8
     */
    private boolean deleteReportById(List<Long> reportId) {

        //通过id查找Report is_delete字段 如果为1则跳过删除该Report操作
        boolean flag = false;
        ArrayList<Long> list = new ArrayList<>();

        for (Long id : reportId) {
            Integer deleteId = findDeleteIdByReportId(id);
            if (deleteId == 0) {
                list.add(id);
            }
        }
        int i = 0;
        for (Long id : list) {
            //删除性格报告
            Integer result = (Integer) Execute.execute(Execute.UPDATE_TYPE, CHARACTER_REPORT_MAPPER_ADDRESS + "deleteById", new Object[]{id});

            //预处理和分析图片 以及  笔记特征删除
            /*  CharacterReport characterReport = queryReportDetails(id);*/
//           boolean result2 = (boolean)HandwritingFeatureService.execute("deleteHandwritingFeature",new Object[]{characterReport.getId()});
            boolean result3 = (boolean) HandwritingImgService.execute("deleteHandwritingImg", new Object[]{id});
         /*  if (result==1&&result2&result3){
               i++;
          }*/
            if (result == 1 && result3) {
                i++;
            }
        }
        if (i == list.size()) {
            flag = true;
        }
        return flag;
    }


    /**
     * 逻辑删除性格报告
     * @param reportIdList: 性格报告集合
     * @return java.lang.Boolean 是否全部删除成功
     * @author He Peng
     * @date 2021/10/28 10:44
     */
    private Boolean deleteReportByIdLogic(List<Long> reportIdList) {
        int res = 0;
        for (Long id : reportIdList) {
            //删除性格报告
            Integer result = (Integer) Execute.execute(Execute.UPDATE_TYPE, CHARACTER_REPORT_MAPPER_ADDRESS + "deleteById", new Object[]{id});
            res += result;
        }
        if (res < reportIdList.size()) {
            throw new HandwritingRuntimeException(ResultCodeEnum.UNKNOWN_ERROR.getCode(), "未知异常，仅删除成功部分性格报告");
        }
        return res > 0;
    }


    /**
     * 通过reportId查找is_deleted字段
     *
     * @param reportId 性格报告编号
     * @return java.lang.Integer
     * @author jiangnan
     * @date 2021/9/6 22:10
     */
    private Integer findDeleteIdByReportId(Long reportId) {

        List<Integer> list = (List<Integer>) Execute.execute(Execute.QUERY_TYPE, CHARACTER_REPORT_MAPPER_ADDRESS + "findDeleteIdByReportId", new Object[]{reportId});
        System.out.println(list.get(0));
        return list.get(0);
    }

    /**
     * 查询性格报告历史(可以分为已分析、未分析和全部)
     * @param id: 用户编号或客户编号
     * @param type: 类型，0：表示用户，1：表示客户
     * @param status: 是否查询未分析的性格报告，-1：查询所有，0：表示查询已分析，1：表示查询未分析性格报告
     * @return java.util.List<com.bluedot.www.core.pojo.DTO.ReportHistoryDTO>
     * @author He Peng
     * @date 2021/10/30 11:07
     */
    private List<ReportHistoryDTO> queryReportHistoryCondition(Long id, Integer type, Integer status) {
        List<ReportHistoryDTO> reportHistoryList = new ArrayList<>();

        Map<String, Object> userIdMap = new HashMap<>(1);
        Map<String, Object> customerIdMap = new HashMap<>(1);
        Map<String, Object> statusMap = new HashMap<>(1);
        String sqlCondition = null;
        if (status == -1) {
            sqlCondition = "= 1 OR 1";
        } else if (status == 0) {
            sqlCondition = "IS NOT NULL";
        } else {
            sqlCondition = "IS NULL";
        }
        statusMap.put("status", sqlCondition);
        if (type == 1) {
            userIdMap.put("userId", "1 OR 1");
            customerIdMap.put("customerId", id);
        } else {
            userIdMap.put("userId", id);
            customerIdMap.put("customerId", "1 OR 1");
        }
        Object[] params = new Object[]{userIdMap, customerIdMap, statusMap};
        List<CharacterReport> reportList = (List<CharacterReport>) Execute.execute(Execute.QUERY_TYPE,
                CHARACTER_REPORT_MAPPER_ADDRESS + "queryReportHistoryCondition", params);
        for (CharacterReport report : reportList) {
            ReportHistoryDTO reportHistory = new ReportHistoryDTO();
            reportHistory.setReportId(report.getId());
            if (report.getReportLink() != null) {
                reportHistory.setReportLink(OSS_ACCESS_URL + report.getReportLink());
            }
            reportHistory.setScore(report.getScore());
            reportHistory.setSummary(report.getSummary());
            reportHistory.setAnalyzeTime(report.getGmtModified());
            // 获取此报告的笔迹图片
            List<HandwritingImg> images = getImageByReportId(report.getId());
            if (images.size() > 0) {
                reportHistory.setWritingTime(images.get(0).getWritingTime());
            }
            List<ImageListDTO> originalUrlList = new ArrayList<>();
            List<ImageListDTO> pretreatedUrlList = new ArrayList<>();
            for (HandwritingImg image : images) {
                originalUrlList.add(new ImageListDTO(image.getId(), OSS_ACCESS_URL + image.getHandwritingImg()));
                pretreatedUrlList.add(new ImageListDTO(image.getId(), OSS_ACCESS_URL + image.getPretreatmentImg()));
            }
            reportHistory.setOriginalImgList(originalUrlList);
            reportHistory.setPretreatedImgList(pretreatedUrlList);

            reportHistoryList.add(reportHistory);
        }
        return reportHistoryList;
    }

    /**
     * 展示数据分析折线图
     *
     * @param userid 用户id
     * @author jiangnan
     * @date 2021/8/8
     */
    private void showReportChart(Long userid) {

    }

    /**
     * 根据性格报告id查找图片
     *
     * @param reportId 性格报告编号
     * @return java.util.List<com.bluedot.www.core.pojo.DO.HandwritingImg>
     * @author jiangnan
     * @date 2021/9/7 16:34
     */
    private List<HandwritingImg> getImageByReportId(Long reportId) {
        return (List<HandwritingImg>) Execute.execute(Execute.QUERY_TYPE,
                CHARACTER_REPORT_MAPPER_ADDRESS + "getImageByReportId", new Object[]{reportId});
    }


    /**
     * 更新性格报告链接
     * @param reportId: 性格报告编号
     * @param reportLink: 性格报告链接
     * @return boolean 是否成功
     * @author He Peng
     * @date 2021/10/19 19:46
     */
    private boolean updateReportLink(Long reportId, String reportLink) {
        Map<String, String> reportIdMap = new HashMap<>(1);
        reportIdMap.put("reportId", String.valueOf(reportId));
        Map<String, String> reportLinkMap = new HashMap<>(1);
        reportLinkMap.put("reportLink", reportLink);
        int res = (int) Execute.execute(Execute.UPDATE_TYPE, CHARACTER_REPORT_MAPPER_ADDRESS + "updateReportLink",
                new Object[]{reportLinkMap, reportIdMap});
        return res > 0;
    }


    /**
     * 根据特征集合等信息，生成性格报告 word 文档
     * @param feature 特征集合
     * @return ByteArrayOutputStream 字节流
     */
    private ByteArrayOutputStream generateCharacterReport(HandwritingFeatureBo feature, Object info,
                                                          List<InputStream> imgList) {
        // 创建实例
        Document doc = new Document();
        InputStream is = CharacterReport.class.getClassLoader().getResourceAsStream(WORD_TEMPLATE_LOCATION);
        // 加载 word 模板
        doc.loadFromStream(is, FileFormat.Docx);
        // 替换特殊字符为指定内容
        Map<String, String> map = new HashMap<>(20);
        map.put("${date}", LocalDate.now().toString());
        wordInfoMapping(map, info);
        wordFeatureMapping(map, feature);
        WordUtil.replaceSpecialWord(doc, map);
        // 替换图片
        WordUtil.replaceTextToImage(doc, HANDWRITING_IMAGES_MARK, imgList, 0.5f);
        // 保存为字节流
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        doc.saveToStream(os, FileFormat.Docx);
        return os;
    }

    /**
     * word文档模板特殊字符与用户信息的关系映射
     * @author He Peng
     * @date 2021/10/18 15:36
     */
    private void wordInfoMapping(Map<String, String> map, Object info) {
        String name = null;
        String age = null;
        String gender = null;
        if (info instanceof User) {
            User user = (User) info;
            name = user.getName();
            LocalDate now = LocalDate.now();
            LocalDate birthday = LocalDate.parse(user.getBirthday().toString());
            Period period = Period.between(birthday, now);
            age = String.valueOf(period.getYears());
            gender = user.getGender() == 0 ? "男" : "女";
        } else if (info instanceof Customer) {
            Customer customer = (Customer) info;
            name = customer.getName();
            LocalDate now = LocalDate.now();
            LocalDate birthday = LocalDate.parse(customer.getBirthday().toString());
            Period period = Period.between(birthday, now);
            age = String.valueOf(period.getYears());
            gender = customer.getGender() == 0 ? "男" : "女";
        } else {
            logger.info("无法处理的对象类型：" + info);
        }
        map.put("${name}", name);
        map.put("${age}", age);
        map.put("${sex}", gender);
    }


    /**
     * word 文档模板特殊字符与笔迹特征值（多张图片的一个平均值）的映射关系
     * @param map: 映射关系
     * @param feature: 笔迹特征值(多张笔迹图片的平均值)
     * @author He Peng
     * @date 2021/10/18 19:18
     */
    private void wordFeatureMapping(Map<String, String> map, HandwritingFeatureBo feature) {
        if (feature == null) {
            logger.info("当前性格报告的笔迹特征值为空");
            return;
        }
        // 计算得到笔迹特征的平均值 (Java 中进行小数运算时精确计算应该使用JDK提供的大数类，此处对于精度要求不高直接进行运算)
        map.put("${pixelCount}", String.valueOf(feature.getPixelCount()));
        map.put("${headerFontSize}", String.valueOf(feature.getHeaderFontSize()));
        map.put("${meanWordArea}", String.valueOf(feature.getMeanWordArea()));
        map.put("${minimumFontArea}", String.valueOf(feature.getMinimumFontArea()));
        map.put("${maximumFontArea}", String.valueOf(feature.getMaximumFontArea()));
        map.put("${meanRowSpacing}", String.valueOf(feature.getMeanRowSpacing()));
        map.put("${titleWordSpacing}", String.valueOf(feature.getTitleWordSpacing()));
        map.put("${minimumRowSpacing}", String.valueOf(feature.getMinimumRowSpacing()));
        map.put("${maximumRowSpacing}", String.valueOf(feature.getMaximumRowSpacing()));
        map.put("${titleGradient}", String.valueOf(feature.getTitleGradient()));
        map.put("${occupancyRate}", String.valueOf(feature.getOccupancyRate()));
        map.put("${theFirstPitch}", String.valueOf(feature.getTheFirstPitch()));
        map.put("${evennessRatio}", String.valueOf(feature.getEvennessRatio()));
        map.put("${horizontalLong}", String.valueOf(feature.getHorizontalLong()));
        map.put("${verticalLong}", String.valueOf(feature.getVerticalLong()));
        map.put("${skimmingLong}", String.valueOf(feature.getSkimmingLong()));
        map.put("${siLong}", String.valueOf(feature.getSiLong()));
        map.put("${horizontalGradient}", String.valueOf(feature.getHorizontalGradient()));
        map.put("${verticalGradient}", String.valueOf(feature.getVerticalGradient()));
        map.put("${skimmingGradient}", String.valueOf(feature.getSkimmingGradient()));
        map.put("${minimumAspectRatio}", String.valueOf(feature.getMinimumAspectRatio()));
        map.put("${maximumAspectRatio}", String.valueOf(feature.getMaximumAspectRatio()));
        map.put("${titleAspectRatio}", String.valueOf(feature.getTitleAspectRatio()));
        map.put("${headerBlank}", String.valueOf(feature.getHeaderBlank()));
        map.put("${footerBlank}", String.valueOf(feature.getFooterBlank()));
        map.put("${leftBlank}", String.valueOf(feature.getLeftBlank()));
        map.put("${rightBlank}", String.valueOf(feature.getRightBlank()));
    }





}
